package com.xplay.xpocker.observer;

import com.xplay.xpocker.constant.MessageNoticeEnum;
import com.xplay.xpocker.room.mahjong.dto.MahjongActionEnum;
import com.xplay.xpocker.room.mahjong.dto.MahjongMessage;
import com.xplay.xpocker.room.message.MessageContent;
import com.xplay.xpocker.room.message.MessageType;
import com.xplay.xpocker.room.voice.VoiceInfo;
import com.xplay.xpocker.socket.WebSocketServerHandler;
import com.xplay.xpocker.util.JacksonStringUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * socket 管理
 */
@Slf4j
@Service
public class ChannelHandlerManager {


    public static final String ROOM_CODE_KEY = "roomCode";

    public static final String USER_CHANNEL_KEY = "userId";

    /**
     * 这个对象保存了所有连接
     * 对应着每个房间
     * 一个订阅对象
     * 订阅对象包含每个订阅者
     */


    public final static ConcurrentHashMap<String, SubscriptionSubject> diskPart = new ConcurrentHashMap<>();

    public void addRoom(String code, ChannelHandlerContext ctx) {
        SubscriptionSubject subject = diskPart.computeIfAbsent(code, (k) -> new SubscriptionSubject());
        log.debug("用户:{},加入房间:{},订阅对象:{}", getUserId(ctx), code, ctx.hashCode());
        subject.attach(ctx);
    }

      /* SubscriptionSubject subject = diskPart.get(code);
        if (subject == null) {
            synchronized (diskPart) {
                subject = diskPart.get(code);
                if (subject == null) {
                    subject = new SubscriptionSubject();
                    diskPart.put(code, subject);
                }
            }
        }
        COPY_ON_WRITE_ARRAY_SET.add(subject);*/


    // SubscriptionSubject subject = diskPart.computeIfAbsent(code, (k) -> new SubscriptionSubject());

    public void heart(String code, ChannelHandlerContext ctx) {
        // log.debug("房间:{},用户:{},心跳...", code, getUserId(ctx));
        SubscriptionSubject subject = diskPart.get(code);
        if (subject == null) {
            return;
        }
        subject.attach(ctx);
    }


    /**
     * 通知房间解散
     *
     * @param code
     */

    public static void dissolution(String code) {
        SubscriptionSubject subject = diskPart.get(code);
        if (subject != null) {
            ConcurrentHashMap<BigDecimal, ChannelHandlerContext> users = subject.getSubjectUsers();
            if (users != null) {
                for (Map.Entry<BigDecimal, ChannelHandlerContext> entry : users.entrySet()) {
                    entry.getValue().writeAndFlush(new TextWebSocketFrame(WebSocketServerHandler.CLOSE_MESSAGE));
                    entry.getValue().channel().close();
                }
            }
            diskPart.remove(code);
        }
    }

    /**
     * 用户上下线 事件
     *
     * @param ctx
     * @param online
     */

    public void userOOEvent(ChannelHandlerContext ctx, boolean online) {

        BigDecimal currentUserId = getUserId(ctx);
        String roomCode = getRoomCode(ctx);
        // 通知其它用户  我上线  或者离线
        notifyOtherUsers(roomCode, currentUserId, online);
        if (!online) {
            // 用户离线了 通知其它用户清理一下 音频
            VoiceInfo voiceInfo = new VoiceInfo();
            voiceInfo.setCurrentUid(currentUserId);
            voiceInfo.setCmd("logout");
            MessageContent.Builder<VoiceInfo> logOutBuilder = MessageContent.Builder.builder(roomCode, MessageType.VOICE.getType(),
                    currentUserId, MessageNoticeEnum.OTHER);
            voiceInfo.addCorrelationUIds(currentUserId);
            logOutBuilder.setBody(voiceInfo);
            notifyMessage(logOutBuilder.build());
        } else {
            addRoom(roomCode, ctx);
        }
    }

    // 构建上下线事件 是否需要持久化????/
    private void notifyOtherUsers(String roomCode, BigDecimal currentUserId, boolean online) {
        try {

            MessageContent<?> messageContent = MessageContent.Builder.builder(roomCode, MessageType.ACTION.getType(),
                            currentUserId, MessageNoticeEnum.OTHER)
                    .setBody(MahjongMessage.Builder.builder()
                            .type(online ? MahjongActionEnum.ONLINE.getAction() :
                                    MahjongActionEnum.OFFLINE.getAction())
                            .exeActionUid(currentUserId)
                            .build()).build();
            notifyMessage(messageContent);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public <T> void notifyMessage(MessageContent<T> messageContent) {
        if (messageContent.getRoomCode() == null) {
            return;
        }
        SubscriptionSubject subject = diskPart.get(messageContent.getRoomCode());
        if (subject != null) {
            subject.notifyMessage(messageContent);
        }
    }


    public static BigDecimal getUserId(ChannelHandlerContext ctx) {
        try {
            Object userId = ctx.channel().attr(AttributeKey.valueOf(USER_CHANNEL_KEY)).get();
            if (userId != null) {
                return new BigDecimal(userId.toString());
            }
            return new BigDecimal("0");
        } catch (Exception e) {
            return new BigDecimal("0");
        }
    }

    public static String getRoomCode(ChannelHandlerContext ctx) {
        Object roomCode = ctx.channel().attr(AttributeKey.valueOf(ROOM_CODE_KEY)).get();
        if (roomCode != null) {
            return roomCode.toString();
        }
        return "";
    }

    /**
     * 不管用户在线与否 都将消息发给用户
     * 房间内 统一 消息发送地方......
     *
     * @param ctx
     * @param obj
     */

    public <T> void senMessage(ChannelHandlerContext ctx, T obj) {
        try {
            if (null != ctx && ctx.channel().isOpen()) {
                String data = JacksonStringUtil.objectToString(obj);
                ctx.channel().writeAndFlush(new TextWebSocketFrame(data));
            } else {
                log.error("------------message send error,user not online{}-----------", obj);
            }
        } catch (Exception e) {
            log.debug("------------message send error,user not online {}-----------", obj);
            e.printStackTrace();
        }
    }

    private static ChannelHandlerManager INSTANCE;

    public static ChannelHandlerManager getInstance() {
        return INSTANCE;
    }

    @PostConstruct
    public void init() {
        INSTANCE = this;
    }

    // 新连接
    // 连接断开

    public void newConn(ChannelHandlerContext ctx) {

    }


}
